﻿#pragma once

//#include <map>
#include <type_traits>
#include <string_view>
#include <algorithm>
#include <shared_mutex>
#include <functional>
#include <fast_io/fast_io_dsal/vector.h>

#include "../../Utils/Utils.h"
#include "../Setting/SettingManager.h"
//#include "CommandsManager.h"

namespace details {

template <class T>
concept has_next_objects = requires(T a) {
							   next_objects(a, nullptr, (size_t)0);
							   next_complete(a);
						   };

struct auto_completion_base_impl {
	virtual constexpr ~auto_completion_base_impl() = default;
	virtual constexpr void* next_objects_impl(fast_io::vector<std::u8string>* cmdlist, size_t order) const noexcept = 0;
	virtual constexpr std::u8string next_complete_impl() const noexcept = 0;
	virtual constexpr auto_completion_base_impl* clone() const = 0;
};

template <has_next_objects T>
struct auto_completion_derv_impl : auto_completion_base_impl {
	T t;
	auto_completion_derv_impl(T&& tt) : t{std::forward<T>(tt)} {}
	virtual constexpr void* next_objects_impl(fast_io::vector<std::u8string>* cmdlist, size_t order) const noexcept override {
		return next_objects(t, cmdlist, order);
	}
	virtual constexpr std::u8string next_complete_impl() const noexcept override{
		return next_complete(t);
	}
	virtual constexpr auto_completion_base_impl* clone() const override {
		return new auto_completion_derv_impl(*this);
	}
};

}  // namespace details

class aliasList_struct {
public:
	::details::auto_completion_base_impl* ptr{};
	constexpr aliasList_struct() noexcept = default;
	template <typename T>
	explicit constexpr aliasList_struct(T&& tt) noexcept : ptr{new ::details::auto_completion_derv_impl<T>(std::forward<T>(tt))} {
	}
	constexpr aliasList_struct(aliasList_struct const& other) : ptr{other.ptr->clone()} {}
	constexpr aliasList_struct& operator=(aliasList_struct const& other) {
		auto temp{other.ptr->clone()};
		delete this->ptr;
		this->ptr = temp;
		return *this;
	}
	constexpr aliasList_struct(aliasList_struct&& other) noexcept : ptr{other.ptr} {
		other.ptr = nullptr;
	}
	constexpr aliasList_struct& operator=(aliasList_struct&& other) noexcept {
		delete this->ptr;
		this->ptr = other.ptr;
		other.ptr = nullptr;
		return *this;
	}
	~aliasList_struct() {
		delete ptr;
	}
};

inline constexpr void* next_objects(aliasList_struct const& p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	return p.ptr->next_objects_impl(cmdlist, order);
}

inline constexpr std::u8string next_complete(aliasList_struct const& p) noexcept {
	return p.ptr->next_complete_impl();
}

struct Empty_Object {
	fast_io::vector<aliasList_struct>* next{};
};

inline constexpr void* next_objects(Empty_Object p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	return p.next;
}

inline constexpr std::u8string next_complete(Empty_Object p) noexcept {
	return std::u8string{};
}

struct stringview_Object {
	std::u8string_view str{};
	fast_io::vector<aliasList_struct>* next{};
};

inline constexpr void* next_objects(stringview_Object p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	if (cmdlist->operator[](order) == p.str) 
		return p.next;
	else
		return nullptr;
}

inline constexpr std::u8string next_complete(stringview_Object p) noexcept {
	return std::u8string{p.str};
}

struct string_Object {
	std::u8string str{};
	fast_io::vector<aliasList_struct>* next{};
};

inline constexpr void* next_objects(string_Object p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	if (cmdlist->operator[](order) == p.str)
		return p.next;
	else
		return nullptr;
}

inline constexpr std::u8string next_complete(string_Object p) noexcept {
	return p.str;
}

template <std::integral _Ty>
struct integral_Object {  // or equal to 
	_Ty min{};
	_Ty max{};
	_Ty temp_num{};
	fast_io::vector<aliasList_struct>* next{};
};

template <std::integral _Ty>
inline constexpr void* next_objects(integral_Object<_Ty> p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	try {
		p.temp_num = fast_io::u8to<_Ty>(cmdlist->operator[](order));
	} catch (const fast_io::error&) {
		return nullptr;
	}

	if (p.temp_num >= p.min && p.temp_num <= p.max) 
		return p.next;
	else
		return nullptr;
}

template <std::integral _Ty>
inline constexpr std::u8string next_complete(integral_Object<_Ty> p) noexcept {
	return fast_io::u8concat(p.temp_num);
}

template <std::floating_point _Ty>
struct floating_point_Object {  // or equal to
	static_assert(std::is_same_v<_Ty, float> || std::is_same_v<_Ty, double> || std::is_same_v<_Ty, long double>, "_Ty must be float, double or long double!");

	_Ty min{};
	bool equaltomin{};
	_Ty max{};
	bool equaltomax{};
	_Ty temp_num{};
	fast_io::vector<aliasList_struct>* next{};
};

template <std::floating_point _Ty>
inline constexpr void* next_objects(floating_point_Object<_Ty> p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	std::u8string& str{cmdlist->operator[](order)};

	try {
		if constexpr (std::is_same_v<_Ty, float>) {
			p.temp_num = std::stof(*reinterpret_cast<std::string*>(&str));
		} else if constexpr (std::is_same_v<_Ty, double>) {
			p.temp_num = std::stod(*reinterpret_cast<std::string*>(&str));
		} else if constexpr (std::is_same_v<_Ty, long double>) {
			p.temp_num = std::stold(*reinterpret_cast<std::string*>(&str));
		} 
	} catch (const std::exception&) {
		return nullptr;
	}

	if (p.equaltomin) {
		if (p.temp_num < p.min) 
			return nullptr;
	} else {
		if (p.temp_num <= p.min)
			return nullptr;
	}

	if (p.equaltomax) {
		if (p.temp_num > p.max)
			return nullptr;
	} else {
		if (p.temp_num >= p.max)
			return nullptr;
	}

	return p.next;
}

template <std::floating_point _Ty>
inline constexpr std::u8string next_complete(floating_point_Object<_Ty> p) noexcept {
	return fast_io::u8concat(p.temp_num);
}

// Judge
struct Empty_Judge {
	fast_io::vector<aliasList_struct>* next{};
	bool (*judge)(fast_io::vector<std::u8string>* cmdlist){};
};

inline constexpr void* next_objects(Empty_Judge p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.judge == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	if (p.judge(cmdlist))
		return p.next;
	else
		return nullptr;
}

inline constexpr std::u8string next_complete(Empty_Judge p) noexcept {
	return std::u8string{};
}

struct stringview_Judge {
	std::u8string_view str{};
	fast_io::vector<aliasList_struct>* next{};
	bool (*judge)(fast_io::vector<std::u8string>* cmdlist){};
};

inline constexpr void* next_objects(stringview_Judge p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.judge == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	if (cmdlist->operator[](order) == p.str) {
		if (p.judge(cmdlist))
			return p.next;
		else
			return nullptr;
	} else
		return nullptr;
}

inline constexpr std::u8string next_complete(stringview_Judge p) noexcept {
	return std::u8string{p.str};
}

struct string_Judge {
	std::u8string str{};
	fast_io::vector<aliasList_struct>* next{};
	bool (*judge)(fast_io::vector<std::u8string>* cmdlist){};
};

inline constexpr void* next_objects(string_Judge p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.judge == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	if (cmdlist->operator[](order) == p.str) {
		if (p.judge(cmdlist))
			return p.next;
		else
			return nullptr;
	} else
		return nullptr;
}

inline constexpr std::u8string next_complete(string_Judge p) noexcept {
	return p.str;
}

template <std::integral _Ty>
struct integral_Judge {  // or equal to
	_Ty min{};
	_Ty max{};
	_Ty temp_num{};
	fast_io::vector<aliasList_struct>* next{};
	bool (*judge)(fast_io::vector<std::u8string>* cmdlist){};
};

template <std::integral _Ty>
inline constexpr void* next_objects(integral_Judge<_Ty> p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.judge == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	try {
		p.temp_num = fast_io::u8to<_Ty>(cmdlist->operator[](order));
	} catch (const fast_io::error&) {
		return nullptr;
	}

	if (p.temp_num >= p.min && p.temp_num <= p.max) {
		if (p.judge(cmdlist))
			return p.next;
		else
			return nullptr;
	} else
		return nullptr;
}

template <std::integral _Ty>
inline constexpr std::u8string next_complete(integral_Judge<_Ty> p) noexcept {
	return fast_io::concat(p.temp_num);
}

template <std::floating_point _Ty>
struct floating_point_Judge {  // or equal to
	static_assert(std::is_same_v<_Ty, float> || std::is_same_v<_Ty, double> || std::is_same_v<_Ty, long double>, "_Ty must be float, double or long double!");
	
	_Ty min{};
	bool equaltomin{};
	_Ty max{};
	bool equaltomax{};
	_Ty temp_num{};
	fast_io::vector<aliasList_struct>* next{};
	bool (*judge)(fast_io::vector<std::u8string>* cmdlist){};
};

template <std::floating_point _Ty>
inline constexpr void* next_objects(floating_point_Judge<_Ty> p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.judge == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	std::u8string& str{cmdlist->operator[](order)};

	try {
		if constexpr (std::is_same_v<_Ty, float>) {
			p.temp_num = std::stof(*reinterpret_cast<std::string*>(&str));
		} else if constexpr (std::is_same_v<_Ty, double>) {
			p.temp_num = std::stod(*reinterpret_cast<std::string*>(&str));
		} else if constexpr (std::is_same_v<_Ty, long double>) {
			p.temp_num = std::stold(*reinterpret_cast<std::string*>(&str));
		}
	} catch (const std::exception&) {
		return nullptr;
	}

	if (p.equaltomin) {
		if (p.temp_num < p.min)
			return nullptr;
	} else {
		if (p.temp_num <= p.min)
			return nullptr;
	}

	if (p.equaltomax) {
		if (p.temp_num > p.max)
			return nullptr;
	} else {
		if (p.temp_num >= p.max)
			return nullptr;
	}

	if (p.judge(cmdlist))
		return p.next;
	else
		return nullptr;
}

template <std::floating_point _Ty>
inline constexpr std::u8string next_complete(floating_point_Judge<_Ty> p) noexcept {
	return fast_io::concat(p.temp_num);
}

// next

struct Empty_Next {
	fast_io::vector<aliasList_struct>* (*next)(fast_io::vector<std::u8string>* cmdlist, size_t order){};
};

inline constexpr void* next_objects(Empty_Next p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.next == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	return p.next(cmdlist, order);
}

inline constexpr std::u8string next_complete(Empty_Next p) noexcept {
	return std::u8string{};
}

struct stringview_Next {
	std::u8string_view str{};
	fast_io::vector<aliasList_struct>* (*next)(fast_io::vector<std::u8string>* cmdlist, size_t order){};
};

inline constexpr void* next_objects(stringview_Next p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.next == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	if (cmdlist->operator[](order) == p.str)
		return p.next(cmdlist, order);
	else
		return nullptr;
}

inline constexpr std::u8string next_complete(stringview_Next p) noexcept {
	return std::u8string{p.str};
}

struct string_Next {
	std::u8string str{};
	fast_io::vector<aliasList_struct>* (*next)(fast_io::vector<std::u8string>* cmdlist, size_t order){};
};

inline constexpr void* next_objects(string_Next p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.next == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	if (cmdlist->operator[](order) == p.str)
		return p.next(cmdlist, order);
	else
		return nullptr;
}

inline constexpr std::u8string next_complete(string_Next p) noexcept {
	return p.str;
}

template <std::integral _Ty>
struct integral_Next {  // or equal to
	_Ty min{};
	_Ty max{};
	_Ty temp_num{};
	fast_io::vector<aliasList_struct>* (*next)(fast_io::vector<std::u8string>* cmdlist, size_t order){};
};

template <std::integral _Ty>
inline constexpr void* next_objects(integral_Next<_Ty> p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.next == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	try {
		p.temp_num = fast_io::u8to<_Ty>(cmdlist->operator[](order));
	} catch (const fast_io::error&) {
		return nullptr;
	}

	if (p.temp_num >= p.min && p.temp_num <= p.max) 
		return p.next(cmdlist, order);
	else
		return nullptr;
}

template <std::integral _Ty>
inline constexpr std::u8string next_complete(integral_Next<_Ty> p) noexcept {
	return fast_io::concat(p.temp_num);
}

template <std::floating_point _Ty>
struct floating_point_Next {  // or equal to
	static_assert(std::is_same_v<_Ty, float> || std::is_same_v<_Ty, double> || std::is_same_v<_Ty, long double>, "_Ty must be float, double or long double!");

	_Ty min{};
	bool equaltomin{};
	_Ty max{};
	bool equaltomax{};
	_Ty temp_num{};
	fast_io::vector<aliasList_struct>* (*next)(fast_io::vector<std::u8string>* cmdlist, size_t order){};
};

template <std::floating_point _Ty>
inline constexpr void* next_objects(floating_point_Next<_Ty> p, fast_io::vector<std::u8string>* cmdlist, size_t order) noexcept {
	if (cmdlist == nullptr || p.next == nullptr) [[unlikely]]
		return nullptr;

	if (order >= cmdlist->size()) [[unlikely]]
		return nullptr;

	std::u8string& str{cmdlist->operator[](order)};

	try {
		if constexpr (std::is_same_v<_Ty, float>) {
			p.temp_num = std::stof(*reinterpret_cast<std::string*>(&str));
		} else if constexpr (std::is_same_v<_Ty, double>) {
			p.temp_num = std::stod(*reinterpret_cast<std::string*>(&str));
		} else if constexpr (std::is_same_v<_Ty, long double>) {
			p.temp_num = std::stold(*reinterpret_cast<std::string*>(&str));
		} 
	} catch (const std::exception&) {
		return nullptr;
	}

	if (p.equaltomin) {
		if (p.temp_num < p.min)
			return nullptr;
	} else {
		if (p.temp_num <= p.min)
			return nullptr;
	}

	if (p.equaltomax) {
		if (p.temp_num > p.max)
			return nullptr;
	} else {
		if (p.temp_num >= p.max)
			return nullptr;
	}

	return p.next(cmdlist, order);
}

template <std::floating_point _Ty>
inline constexpr std::u8string next_complete(floating_point_Next<_Ty> p) noexcept {
	return fast_io::concat(p.temp_num);
}

// i command
class ICommand {
public:
	std::u8string_view _command{};
	std::u8string_view _usage{};
	std::array<std::u8string_view, languagesize> _description{};
	fast_io::vector<aliasList_struct> aliasList{};
	std::shared_mutex iCommandLook{};

public:
	void (*init)(ICommand*){nullptr};
	bool (*onExecute)(const fast_io::vector<std::u8string>& args){nullptr};

public:
	static float assertFloat(std::u8string_view string);
	inline static constexpr int assertInt(std::u8string_view string) { return fast_io::u8to<int>(string); }
	// void registerAlias(std::u8string_view str);
	// void clientMessageF(std::u8string_view str);

public:
	ICommand(std::u8string_view com, std::u8string_view usage, std::array<std::u8string_view, languagesize> des, std::initializer_list<std::u8string> nameTable = std::initializer_list<std::u8string>{}, fast_io::vector<aliasList_struct> al = fast_io::vector<aliasList_struct>{}, void (*_init)(ICommand*) = nullptr, bool (*_onExecute)(const fast_io::vector<std::u8string>&) = nullptr);
	void _init();
	bool _onExecute(const fast_io::vector<std::u8string>& args);
};
